//package com.Cychat.doctor.consumer;
//
//import com.Cychat.commons.entity.pojo.Hospital;
//import com.Cychat.doctor.config.TopicMQConfig;
//import com.rabbitmq.client.Channel;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.ai.document.Document;
//import org.springframework.ai.vectorstore.redis.RedisVectorStore;
//import org.springframework.amqp.core.Message;
//import org.springframework.amqp.rabbit.annotation.RabbitListener;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.stereotype.Component;
//import org.springframework.util.Assert;
//
//import java.io.IOException;
//import java.math.BigDecimal;
//import java.text.SimpleDateFormat;
//import java.util.*;
//import java.util.stream.Collectors;
//
///**
// * 医院信息向量消费处理器
// * 功能：监听医院消息队列，将医院信息向量化后永久存入向量库和Redis（用于AI查询）
// * 依赖RedisVectorStore内部处理
// *
// * @author 谢宝辉
// * @date 2025/11/5 15:55
// */
//@Component
//@Slf4j
//public class VectorConsumer {
//
//    // Redis缓存前缀（统一管理，便于维护）
//    private static final String REDIS_HOSPITAL_VECTOR_PREFIX = "hospital:vector:";
//    // 时间格式化器（与实体类@JsonFormat注解严格一致，全局复用）
//    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//
//    static {
//        DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT+8")); // 静态块初始化时区，避免构造器重复执行
//    }
//
//    @Autowired
//    private RedisTemplate<String, String> redisTemplate;
//    @Autowired
//    private RedisVectorStore redisVectorStore;// 依赖RedisVectorStore内部自动完成向量化
//
//    /**
//     * 构造器注入依赖
//     */
//    public VectorConsumer(RedisTemplate<String, String> redisTemplate,
//                          RedisVectorStore redisVectorStore) {
//        this.redisTemplate = redisTemplate;
//        this.redisVectorStore = redisVectorStore;
//    }
//
//    /**
//     * 监听医院向量化队列，处理单条医院信息的向量化
//     * 特性：手动ACK、异常重试、死信队列兼容、数据校验、永久存储
//     */
//    @RabbitListener(queues = TopicMQConfig.HOSPITAL_VECTOR_PENDING_QUEUE)
//    public void handleHospitalVector(Hospital hospital, Channel channel, Message message) throws IOException {
//        long deliveryTag = message.getMessageProperties().getDeliveryTag();
//        Integer hospitalId = hospital.getHospitalId();
//        log.info("开始处理医院向量化（永久存储）：hospitalId={}, 医院名称={}", hospitalId, hospital.getName());
//
//        try {
//            // 1. 数据校验（避免无效数据处理，确保AI查询的数据质量）
//            validateHospitalData(hospital);
//
//            // 2. 构建向量文档（文本内容+元数据，与错题文档结构一致）
//            Document vectorDocument = buildVectorDocument(hospital);
//
//            // 3. 永久保存到向量库（依赖RedisVectorStore内部自动向量化，与错题逻辑一致）
//            saveToVectorStore(Collections.singletonList(vectorDocument));
//
//            // 4. 永久缓存向量到Redis（用于AI快速查询，无过期时间）
//            cacheVectorToRedis(String.valueOf(hospitalId), vectorDocument);
//
//            // 5. 手动ACK：处理成功，确认消息删除
//            channel.basicAck(deliveryTag, false); // false：不批量确认，避免批量处理导致的消息丢失
//            log.info("医院向量化处理成功（已永久存储）：hospitalId={}", hospitalId);
//
//        } catch (IllegalArgumentException e) {
//            // 数据校验失败：拒绝消息并丢弃（无需重试，无效数据不存入存储）
//            log.error("医院数据校验失败（拒绝重试，不存储）：hospitalId={}, 错误信息={}", hospitalId, e.getMessage());
//            channel.basicReject(deliveryTag, false); // false：不重新入队，直接丢弃或进入死信队列
//        } catch (Exception e) {
//            // 业务处理异常：重试机制（与错题逻辑一致，最大3次重试）
//            int retryCount = getRetryCount(message);
//            if (retryCount < 3) {
//                log.error("医院向量化处理失败，将重试（当前重试次数={}）：hospitalId={}, 错误信息={}",
//                        retryCount, hospitalId, e.getMessage(), e);
//                channel.basicNack(deliveryTag, false, true); // true：重新入队重试
//            } else {
//                // 重试耗尽：进入死信队列
//                log.error("医院向量化处理失败，重试次数耗尽（进入死信队列）：hospitalId={}, 错误信息={}",
//                        hospitalId, e.getMessage(), e);
//                channel.basicNack(deliveryTag, false, false);
//            }
//        }
//    }
//
//    /**
//     * 数据校验：核心字段非空
//     */
//    private void validateHospitalData(Hospital hospital) {
//        Assert.notNull(hospital.getHospitalId(), "医院ID（hospitalId）不能为空");
//        Assert.hasText(hospital.getName(), "医院名称（name）不能为空");
//        Assert.hasText(hospital.getAddress(), "医院地址（address）不能为空");
//    }
//
//    /**
//     * 构建向量文档（文档结构：唯一ID+文本内容+元数据）
//     */
//    private Document buildVectorDocument(Hospital hospital) {
//        return new Document(
//                getHospitalVectorDocId(hospital), // 唯一文档ID（避免重复存储）
//                buildVectorContent(hospital),     // 向量化文本内容
//                buildVectorMetadata(hospital)     // 元数据（用于筛选查询）
//        );
//    }
//
//    /**
//     * 保存到向量库（重试+批量处理逻辑）
//     */
//    private void saveToVectorStore(List<Document> documents) {
//        if (documents == null || documents.isEmpty()) {
//            log.info("无有效向量文档可存储");
//            return;
//        }
//        // 带重试机制的向量存储（与错题retryAddToVectorStore逻辑一致）
//        retryAddToVectorStore(documents, 3);
//    }
//
//    /**
//     * 带重试机制的向量存储
//     */
//    private void retryAddToVectorStore(List<Document> documents, int maxRetries) {
//        for (int i = 0; i < maxRetries; i++) {
//            try {
//                redisVectorStore.add(documents);
//                log.debug("向量库存储成功，文档数量：{}", documents.size());
//                return;
//            } catch (Exception e) {
//                log.warn("第 {} 次尝试添加文档到向量存储失败: {}", i + 1, e.getMessage());
//                if (i == maxRetries - 1) {
//                    throw new RuntimeException("向量库存储失败（重试耗尽）", e);
//                }
//                try {
//                    Thread.sleep(1000 * (i + 1)); // 递增延迟重试
//                } catch (InterruptedException ie) {
//                    Thread.currentThread().interrupt();
//                    throw new RuntimeException("向量存储重试被中断", ie);
//                }
//            }
//        }
//    }
//
//    /**
//     * 永久缓存向量到Redis（无过期时间，用于AI快速查询）
//     */
//    private void cacheVectorToRedis(String hospitalId, Document document) {
//        try {
//            String redisKey = REDIS_HOSPITAL_VECTOR_PREFIX + hospitalId;
//            // 存储文档JSON（与错题存储结构一致，便于后续读取）
//            String documentJson = com.alibaba.fastjson.JSON.toJSONString(document);
//            redisTemplate.opsForValue().set(redisKey, documentJson);
//            log.debug("Redis向量永久缓存成功：key={}", redisKey);
//        } catch (Exception e) {
//            log.warn("Redis向量缓存失败（不影响核心AI查询）：hospitalId={}, 错误信息={}", hospitalId, e.getMessage(), e);
//        }
//    }
//
//    /**
//     * 获取消息重试次数（从RabbitMQ消息头获取）
//     */
//    private int getRetryCount(Message message) {
//        Map<String, Object> headers = message.getMessageProperties().getHeaders();
//        return headers.getOrDefault("x-retried-count", 0) instanceof Integer ?
//                (Integer) headers.get("x-retried-count") : 0;
//    }
//
//    /**
//     * 生成医院向量文档唯一ID
//     */
//    private String getHospitalVectorDocId(Hospital hospital) {
//        return "hospital:vector:" + hospital.getHospitalId(); // 与错题ID规则保持一致（业务:类型:ID）
//    }
//
//    /**
//     * 构建向量文本内容（突出关键查询字段：ID、名称、地址、联系电话、地理坐标、营业时间、介绍）
//     */
//    private String buildVectorContent(Hospital hospital) {
//        return new StringBuilder()
//                .append("医院ID：").append(hospital.getHospitalId()).append("\n")
//                .append("医院名称：").append(Optional.ofNullable(hospital.getName()).orElse("未知医院")).append("\n")
//                .append("详细地址：").append(Optional.ofNullable(hospital.getAddress()).orElse("无地址信息")).append("\n")
//                .append("联系电话：").append(Optional.ofNullable(hospital.getPhone()).orElse("无联系电话")).append("\n")
//                .append("地理坐标：纬度 ").append(Optional.ofNullable(hospital.getLatitude()).orElse(BigDecimal.ZERO))
//                .append("，经度 ").append(Optional.ofNullable(hospital.getLongitude()).orElse(BigDecimal.ZERO)).append("\n")
//                .append("营业时间：").append(Optional.ofNullable(hospital.getOpeningHours()).orElse("无营业时间信息")).append("\n")
//                .append("医院介绍：").append(Optional.ofNullable(hospital.getDescription()).orElse("无医院介绍")).append("\n")
//                .toString();
//    }
//
//    /**
//     * 构建向量元数据（字段结构化，与查询字段保持一致）
//     */
//    private Map<String, Object> buildVectorMetadata(Hospital hospital) {
//        Map<String, Object> metadata = new HashMap<>(16);
//
//        // 基础信息
//        metadata.put("hospitalId", hospital.getHospitalId());
//        metadata.put("name", Optional.ofNullable(hospital.getName()).orElse("未知医院"));
//        metadata.put("address", Optional.ofNullable(hospital.getAddress()).orElse("无地址信息"));
//        metadata.put("phone", Optional.ofNullable(hospital.getPhone()).orElse("无联系电话"));
//
//        // 地理坐标
//        metadata.put("latitude", Optional.ofNullable(hospital.getLatitude()).orElse(BigDecimal.ZERO).doubleValue());
//        metadata.put("longitude", Optional.ofNullable(hospital.getLongitude()).orElse(BigDecimal.ZERO).doubleValue());
//
//        // 业务信息
//        metadata.put("openingHours", Optional.ofNullable(hospital.getOpeningHours()).orElse("无营业时间信息"));
//        metadata.put("description", Optional.ofNullable(hospital.getDescription()).orElse("无医院介绍"));
//
//        // 系统信息（与错题元数据风格一致）
//        metadata.put("vectorCreateTime", DATE_FORMAT.format(new Date()));
//        metadata.put("docType", "hospital");
//
//        return metadata;
//    }
//
//    /**
//     * 批量保存医院信息到向量库（支持批量同步）
//     */
//    public void batchSaveToVectorStore(List<Hospital> hospitals) {
//        if (hospitals == null || hospitals.isEmpty()) {
//            log.info("无有效医院信息可向量化存储");
//            return;
//        }
//
//        // 过滤无效数据 + 构建向量文档（参考错题过滤逻辑）
//        List<Document> vectorDocuments = hospitals.stream()
//                .filter(Objects::nonNull)
//                .filter(h -> h.getHospitalId() != null)
//                .filter(this::isValidHospital)
//                .map(this::buildVectorDocument)
//                .collect(Collectors.toList());
//
//        if (vectorDocuments.isEmpty()) {
//            log.info("过滤后无有效医院信息，跳过向量库存储");
//            return;
//        }
//
//        // 分批处理（参考错题saveToVectorStoreWithBatch逻辑，批次大小10）
//        int batchSize = 10;
//        for (int i = 0; i < vectorDocuments.size(); i += batchSize) {
//            int endIndex = Math.min(i + batchSize, vectorDocuments.size());
//            List<Document> batchDocs = vectorDocuments.subList(i, endIndex);
//            try {
//                saveToVectorStore(batchDocs);
//                // 批量缓存到Redis
//                batchCacheVectorToRedis(batchDocs);
//                log.info("批量向量化第{}批次完成，处理医院数量：{}", (i / batchSize) + 1, batchDocs.size());
//            } catch (Exception e) {
//                log.error("批量向量化第{}批次失败", (i / batchSize) + 1, e);
//                throw new RuntimeException("批量向量存储失败", e);
//            }
//        }
//    }
//
//    /**
//     * 自定义医院有效性过滤规则
//     */
//    private boolean isValidHospital(Hospital hospital) {
//        return hospital.getName() != null && hospital.getAddress() != null;
//    }
//
//    /**
//     * 批量缓存向量到Redis
//     */
//    private void batchCacheVectorToRedis(List<Document> vectorDocuments) {
//        Map<String, String> cacheMap = new HashMap<>(vectorDocuments.size());
//        for (Document doc : vectorDocuments) {
//            String hospitalId = doc.getMetadata().get("hospitalId").toString();
//            String redisKey = REDIS_HOSPITAL_VECTOR_PREFIX + hospitalId;
//            cacheMap.put(redisKey, com.alibaba.fastjson.JSON.toJSONString(doc));
//        }
//        // 批量存入Redis（无过期时间）
//        redisTemplate.opsForValue().multiSet(cacheMap);
//        log.info("批量Redis缓存完成，缓存数量：{}", cacheMap.size());
//    }
//}